﻿using Microsoft.Win32.TaskScheduler;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using Trigger = Microsoft.Win32.TaskScheduler.Trigger;

/*
 * 源码己托管: http://gitee.com/dlgcy/dotnetcodes
 * 版本：2022年9月1日
 */
namespace DotNet.Utilities
{
    /// <summary>
    /// 任务计划帮助类
    /// </summary>
    public class TaskSchedulerHelper
    {
        #region 基本方法 https://www.cnblogs.com/qtiger/p/14814457.html

        /// <summary>
        /// 创建一个以指定日期时间为触发条件的计划任务
        /// </summary>
        /// <param name="subFolderName">文件夹名称</param>
        /// <param name="taskName">任务名称</param>
        /// <param name="taskDescription">任务描述</param>
        /// <param name="path">应用程序路径</param>
        /// <param name="startBoundary">指定日期时间</param>
        /// <param name="arguments">应用程序参数</param>
        public static void CreateOnceRunTask(string subFolderName, string taskName, string taskDescription, string path, DateTime startBoundary, string arguments)
        {
            using (TimeTrigger trigger = new TimeTrigger(startBoundary))
            {
                using (ExecAction action = new ExecAction(path, arguments, null))
                {
                    CreateTask(subFolderName, taskName, taskDescription, trigger, action);
                }
            }
        }

        /// <summary>
        /// 创建一个计划任务
        /// </summary>
        /// <param name="subFolderName">文件夹名称</param>
        /// <param name="taskName">任务名称</param>
        /// <param name="taskDescription">任务描述</param>
        /// <param name="trigger">触发条件</param>
        /// <param name="action">执行任务</param>
        public static void CreateTask(string subFolderName, string taskName, string taskDescription, Trigger trigger, Microsoft.Win32.TaskScheduler.Action action)
        {
            using (TaskService ts = new TaskService())
            {
                using (TaskDefinition td = ts.NewTask())
                {
                    td.RegistrationInfo.Description = taskDescription;
                    //http://stackoverflow.com/questions/8170236/how-to-set-run-only-if-logged-in-and-run-as-with-taskscheduler-in-c
                    //td.Principal.UserId = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
                    //td.Principal.LogonType = TaskLogonType.InteractiveToken;
                    td.Principal.RunLevel = TaskRunLevel.Highest;
                    td.Triggers.Add(trigger);
                    td.Actions.Add(action);

                    TaskFolder folder = ts.RootFolder;
                    if (!string.IsNullOrWhiteSpace(subFolderName))
                    {
                        if (!folder.SubFolders.Exists(subFolderName))
                        {
                            folder = folder.CreateFolder(subFolderName);
                        }
                        else
                        {
                            folder = folder.SubFolders[subFolderName];
                        }
                    }
                    folder.RegisterTaskDefinition(taskName, td);
                    folder.Dispose();
                    folder = null;
                }
            }
        }

        /// <summary>
        /// 删除某一个子文件夹
        /// </summary>
        /// <param name="folderName">绝对路径</param>
        public static void DeleteSubFolders(string folderName = @"\Test")
        {
            // Get the service on the local machine 
            using (TaskService taskService = new TaskService())
            {
                using (TaskFolder folder = taskService.GetFolder(folderName))
                {
                    if (folder.Tasks.Count > 0)
                    {
                        throw new InvalidOperationException("TASKS_EXIST");
                    }
                    if (folder.SubFolders.Count > 0)
                    {
                        throw new InvalidOperationException("SUB_FOLDERS_EXIST");
                    }
                    using (TaskFolder folderParent = taskService.GetFolder(folder.Path.Replace(@"\" + folder.Name, "")))
                    {
                        folderParent.DeleteFolder(folder.Name);
                    }
                }
            }
        }

        /// <summary>
        /// 获取某一个文件夹下面的所有子文件夹
        /// </summary>
        /// <param name="folderName">传null表示根文件夹</param>
        /// <returns></returns>
        public static IEnumerable<TaskFolder> GetAllSubFolder(string folderName = @"\Test")
        {
            using (TaskService taskService = new TaskService())
            {
                TaskFolder folder = taskService.RootFolder;
                if (!string.IsNullOrWhiteSpace(folderName))
                {
                    folder = taskService.GetFolder(folderName);
                }
                foreach (TaskFolder subFolder in folder.SubFolders)
                {
                    yield return subFolder;
                }
            }
        }

        /// <summary>
        /// 删除某一个文件夹下面的某一个任务
        /// </summary>
        /// <param name="taskName">要删除的任务名称</param>
        /// <param name="folderName">传null表示根文件夹</param>
        public static void DeleteTask(string folderName, string taskName)
        {
            using (TaskService taskService = new TaskService())
            {
                TaskFolder folder = taskService.RootFolder;
                // 判断文件夹和任务是否存在
                if (folder.SubFolders.Exists(folderName) && folder.SubFolders[folderName].Tasks.Exists(taskName))
                {
                    folder.SubFolders[folderName].DeleteTask(taskName);
                }
                folder.Dispose();
                folder = null;
            }
        }

        /// <summary>
        /// 获取所有任务
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<Task> GetAllTask()
        {
            using (TaskService taskService = new TaskService())
            {
                return taskService.AllTasks;
            }
        }

        /// <summary>
        /// 获取某一个文件夹下面的所有任务
        /// </summary>
        /// <param name="folderName">传null表示根文件夹</param>
        /// <returns></returns>
        public static List<Task> GetAllTask(string folderName = @"\Test")
        {
            using (TaskService taskService = new TaskService())
            {
                TaskFolder folder = taskService.RootFolder;
                if (!string.IsNullOrWhiteSpace(folderName) && folder.SubFolders.Exists(folderName))
                {
                    folder = taskService.GetFolder(folderName);
                    return folder.AllTasks.ToList();
                }
                return new List<Task>(0);
            }
        }

        /// <summary>
        /// 查找符合条件的所有Task
        /// </summary>
        /// <param name="regex"></param>
        /// <param name="searchAllFolders"></param>
        /// <returns></returns>
        public static IEnumerable<Task> GetAllTask(Regex regex, bool searchAllFolders = true)
        {
            using (TaskService taskService = new TaskService())
            {
                return taskService.FindAllTasks(regex, searchAllFolders);
            }
        }

        /// <summary>
        /// 查找符合条件的Task
        /// </summary>
        /// <param name="taskName"></param>
        /// <param name="searchAllFolders"></param>
        /// <returns></returns>
        public static Task GetTask(string taskName, bool searchAllFolders = true)
        {
            using (TaskService taskService = new TaskService())
            {
                return taskService.FindTask(taskName, searchAllFolders);
            }
        }

        /// <summary>
        /// 指定文件夹下是否存在指定任务
        /// </summary>
        /// <param name="folderName">文件夹</param>
        /// <param name="taskName">任务名</param>
        /// <returns>bool</returns>
        public static bool IsExist(string folderName, string taskName)
        {
            using (TaskService taskService = new TaskService())
            {
                TaskFolder folder = taskService.RootFolder;
                // 判断文件夹和任务是否存在
                return folder.SubFolders.Exists(folderName) && folder.SubFolders[folderName].Tasks.Exists(taskName);
            }
        }

        /// <summary>
        /// [dlgcy] 根目录下是否存在任务
        /// </summary>
        /// <param name="taskName">任务名称</param>
        /// <returns></returns>
        public static bool IsExist(string taskName)
        {
            using TaskService taskService = new TaskService();
            using TaskFolder folder = taskService.RootFolder;
            return folder.Tasks.Exists(taskName);
        }

        #endregion

        #region 其它方法

        /// <summary>
        /// 切换是否跟随系统启动
        /// </summary>
        /// <param name="isStartWithSys">是否随系统启动（默认为 true）</param>
        /// <param name="isBeforeLogon">是否在登陆前启动（[默认]true-在登陆前启动(以System身份)，UI界面将不显示；false-在任意用户登陆后启动，可显示UI界面）</param>
        public static bool SwitchStartupWithSystem(bool isStartWithSys = true, bool isBeforeLogon = true)
        {
            bool result = true;

            try
            {
                string taskName = $"{Process.GetCurrentProcess().ProcessName}_Startup";
                var task = TaskService.Instance.FindTask(taskName);
                string exePath = Process.GetCurrentProcess().MainModule?.FileName;
                if (string.IsNullOrWhiteSpace(exePath))
                {
                    exePath = Assembly.GetEntryAssembly()?.FullName;
                }

                if (isStartWithSys) //开机自启
                {
                    if (task == null)
                    {
                        string desc = "Auto Startup";
                        if (isBeforeLogon)
                        {
                            task = TaskService.Instance.AddTask(taskName, QuickTriggerType.Boot, exePath, description: desc);

                            //A valid system account name must be supplied for TaskLogonType.ServiceAccount.
                            //Valid entries are "NT AUTHORITY\\SYSTEM", "SYSTEM", "NT AUTHORITY\\LOCALSERVICE", or "NT AUTHORITY\\NETWORKSERVICE". (Parameter 'userId')
                            task.Definition.Principal.LogonType = TaskLogonType.ServiceAccount; //登陆方式，ServiceAccount 可以在未登陆时就启动;
                            task.Definition.Principal.UserId = "SYSTEM";                        //登陆方式为 ServiceAccount 时必须指定用户ID;
                        }
                        else
                        {
                            task = TaskService.Instance.AddTask(taskName, QuickTriggerType.Logon, exePath, description: desc);
                        }

                        task.Definition.Settings.Compatibility = TaskCompatibility.V2_3;    //兼容性，V2_3 为 Win10;
                        task.Definition.Settings.WakeToRun = true;
                        task.Definition.Settings.Hidden = false;
                        task.Definition.Principal.RunLevel = TaskRunLevel.Highest;

                        task.RegisterChanges();
                        Console.WriteLine($"[开机自启] 添加成功");
                    }
                    else
                    {
                        Console.WriteLine($"[开机自启] 该任务已存在");
                    }
                }
                else //取消开机自启
                {
                    if (task != null)
                    {
                        task.Folder.DeleteTask(taskName);
                        Console.WriteLine($"[关闭-开机自启] 成功");
                    }
                    else
                    {
                        Console.WriteLine($"[关闭-开机自启] 无相关任务");
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"切换开机自启【{(isStartWithSys ? "开" : "关")}】失败：{ex}");
                result = false;
            }

            return result;
        }

        /// <summary>
        /// 是否已经存在开机自启项
        /// </summary>
        /// <returns></returns>
        public static bool IsExistStartupTask()
        {
            string taskName = $"{Process.GetCurrentProcess().ProcessName}_Startup";
            return IsExist(taskName);
        }

        #endregion
    }
}
